package com.xiongjie;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.exception.MultiObjectDeleteException;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.transfer.Copy;
import com.qcloud.cos.transfer.Download;
import com.qcloud.cos.transfer.TransferManager;
import com.qcloud.cos.transfer.Upload;
import com.qcloud.cos.model.DeleteObjectsRequest.KeyVersion;
import com.qcloud.cos.model.DeleteObjectsResult.DeletedObject;
import com.qcloud.cos.exception.MultiObjectDeleteException.DeleteError;

import java.io.*;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Application {

    private static String appid;
    private static String secretid;
    private static String secretkey;
    private static String region;
    private static String remoteregion;
    private static String bucketname;
    private static String anotherbucketname;
    private static String remotebucketname;
    private static COSClient cosclient;
    private static COSCredentials cred;


    public static void main(String[] args) {
//        createBucket("xiongjie");
        uploadObjectByFile("src/main/resources/1.txt", "1.txt", null);
//        uploadObjectByStream(new byte[10], "stream.txt", null);
//        uploadObjectByPart("part.txt",null);
//        uploadObjectByTransferManager("src/main/resources/2.txt", "2.txt", null);
//        uploadObjectByTransferManager("src/main/resources/《最长的思念》.mp4","《最长的思念》.mp4",null);
//        downloadObjectBySimple("1.txt", "xiongjie");
//        downloadObjectByTransferManager("1.txt","src/main/resources/3.txt",null);
//        deleteCosObjectBySimple("1.txt",null);

//        ArrayList<KeyVersion> keyList = new ArrayList<>();
//        keyList.add(new KeyVersion("2.txt"));
//        keyList.add(new KeyVersion("part.txt"));
//        keyList.add(new KeyVersion("stream.txt"));
//        keyList.add(new KeyVersion("《最长的思念》.mp4"));
//        deleteCosObjectByBatch(keyList,null);

//        deleteBucket(null);
//        listCosObjectBySimple(null);
//        listCosObjectByBatch(null);

//        copyObjectBySameBucket("1.txt","2.txt",null);
//        copyObjectBySameBucket("1.txt","/hello/1.txt",null);
//        copyObjectByDifferentBucket("1.txt",null,"1.txt",null);
//        copyObjectByTransferManagerAndSameRegion("2.txt",null,"2.txt",null);
//        copyObjectByTransferManagerAndSameRegion("《最长的思念》.mp4",null,"《最长的思念》.mp4",null);

        //目前这部分代码有问题
//        copyObjectByTransferManagerAndDifferentRegion("1.txt",null,"1.txt",null);
    }

    /**
     * 1.获取APPID，SecretId，SecretKey
     */
    public static void getProperties() {
        Properties pro = new Properties();
        InputStream in = Application.class.getClassLoader().getResourceAsStream("cos.properties");
        try {
            pro.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        appid = pro.getProperty("APPID");
        secretid = pro.getProperty("SecretId");
        secretkey = pro.getProperty("SecretKey");
        region = pro.getProperty("Region");
        remoteregion=pro.getProperty("RemoteRegion");
        bucketname = pro.getProperty("BucketName");
        anotherbucketname = pro.getProperty("AnotherBucketName");
        remotebucketname=pro.getProperty("RemoteBucketName");
    }

    /**
     * 2.获取COSClient
     */
    public static void getCOSClient() {
        cred = new BasicCOSCredentials(secretid, secretkey);
        ClientConfig clientConfig = new ClientConfig(new Region(region));
        cosclient = new COSClient(cred, clientConfig);
    }

    /**
     * 3.创建存储桶
     *
     * @param name
     * @return
     */
    public static Bucket createBucket(String name) {
        getProperties();
        getCOSClient();

        String bucketName = name + "-" + appid;
        CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
        createBucketRequest.setCannedAcl(CannedAccessControlList.Private);

        Bucket bucket = null;
        try {
            bucket = cosclient.createBucket(createBucketRequest);
            System.out.println(bucket.toString());
        } catch (Exception e) {
            System.out.println("名字为" + name + "存储桶已经创建！请修改名字");
        }

        return bucket;
    }

    /**
     * 4.上传单个文件,可以覆盖上传。
     */
    public static void uploadObjectByFile(String srcPath, String descPath, String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        String key = descPath;
        File localFile = new File(srcPath);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketname, key, localFile);
        putObjectRequest.setStorageClass(StorageClass.Standard);
        try {
            PutObjectResult putObjectResult = cosclient.putObject(putObjectRequest);
            System.out.println(putObjectResult.getETag());

        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        }
        cosclient.shutdown();
    }

    /**
     * 5.数据流的方式上传单个文件
     */
    public static void uploadObjectByStream(byte[] bytes, String descPath, String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        InputStream input = new ByteArrayInputStream(bytes);
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(10);
        objectMetadata.setContentType("text/plain");

        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketname, descPath, input, objectMetadata);
        putObjectRequest.setStorageClass(StorageClass.Standard);
        try {
            PutObjectResult putObjectResult = cosclient.putObject(putObjectRequest);
            System.out.println(putObjectResult.getETag());
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        }
        cosclient.shutdown();
    }

    //6.java sdk没有批量上传的方法，Javascript有

    /**
     * 7.使用分块方法上传大文件，以20M为界限.一般将对象转化为流的形式
     * 步骤：uploadId表示整个分块的标识
     * <p>
     * initiateMultipartUpload 初始化分块获取uploadid
     * 使用 uploadPart 上传分块数据
     * completeMultipartUpload 完成分块上传
     */
    public static void uploadObjectByPart(String descPath, String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketname, descPath);
        request.setStorageClass(StorageClass.Standard);

        String uploadId = null;
        try {
            InitiateMultipartUploadResult initResult = cosclient.initiateMultipartUpload(request);
            uploadId = initResult.getUploadId();
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        }

        //这里初始化一个空的队列，用于保存已上传的分片信息, 代码不能直接运行，需要通过之前的uploadpart或者list parts的结果获取,加入到partEtags队列中
        List<PartETag> partETags = new LinkedList<>();

        for (int i = 0; i < 10; i++) {
            // 生成要上传的数据, 这里初始化一个1M的数据
            byte data[] = new byte[1024 * 1024];
            UploadPartRequest uploadPartRequest = new UploadPartRequest();
            uploadPartRequest.setBucketName(bucketname);
            uploadPartRequest.setKey(descPath);
            uploadPartRequest.setUploadId(uploadId);
            uploadPartRequest.setInputStream(new ByteArrayInputStream(data));
            uploadPartRequest.setPartSize(data.length);
            uploadPartRequest.setPartNumber(i);

            try {
                UploadPartResult uploadPartResult = cosclient.uploadPart(uploadPartRequest);
                partETags.add(uploadPartResult.getPartETag());
            } catch (CosServiceException e) {
                e.printStackTrace();
            } catch (CosClientException e) {
                e.printStackTrace();
            }
        }

        CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, descPath, uploadId, partETags);
        try {
            CompleteMultipartUploadResult completeResult = cosclient.completeMultipartUpload(completeMultipartUploadRequest);
            System.out.println(completeResult.getETag());
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        }
        cosclient.shutdown();
    }

    /**
     * 7.使用高级方法上传对象，自动根据对象大小选择简单上传还是分块上传。
     * 而且支持暂停功能，并且支持查看上传进度百分数
     * TransferManager
     */
    public static void uploadObjectByTransferManager(String srcPath, String descPath, String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        ExecutorService threadPool = Executors.newFixedThreadPool(32);
        TransferManager transferManager = new TransferManager(cosclient, threadPool);

        File localFile = new File(srcPath);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketname, descPath, localFile);
        try {
            // 返回一个异步结果Upload, 可同步的调用waitForUploadResult等待upload结束, 成功返回UploadResult, 失败抛出异常.
            long startTime = System.currentTimeMillis();

            Upload upload = transferManager.upload(putObjectRequest);
            UploadResult uploadResult = upload.waitForUploadResult();

            long endTime = System.currentTimeMillis();
            System.out.println("used time: " + (endTime - startTime) / 1000);
            System.out.println(uploadResult.getETag());
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        transferManager.shutdownNow();
        cosclient.shutdown();
    }

    /**
     * 8.简单方法下载对象。不知道被下载到何处了
     * 这里存储桶不包括APPID
     */
    public static void downloadObjectBySimple(String srcPath, String bucketName) {
        getProperties();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        COSCredentials cred = new BasicCOSCredentials(appid, secretid, secretkey);
        ClientConfig clientConfig = new ClientConfig(new Region(region));
        COSClient cosclient = new COSClient(cred, clientConfig);

        try {
            COSObject cosObject = cosclient.getObject(bucketname, srcPath);
            COSObjectInputStream cosObjectInput = cosObject.getObjectContent();

            cosObjectInput.close();
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 9.高级方法下载对象
     * TransferManager
     */
    public static void downloadObjectByTransferManager(String srcPath, String descpath, String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        ExecutorService threadPool = Executors.newFixedThreadPool(32);
        TransferManager transferManager = new TransferManager(cosclient, threadPool);

        File downloadFile = new File(descpath);
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucketname, srcPath);
        try {
            Download download = transferManager.download(getObjectRequest, downloadFile);
            download.waitForCompletion();
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        transferManager.shutdownNow();
        cosclient.shutdown();
    }

    /**
     * 🌿🌿🌿🌿🌿
     * 10.删除单个对象
     */
    public static void deleteCosObjectBySimple(String srcPath, String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        try {
            cosclient.deleteObject(bucketname, srcPath);
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        }
        cosclient.shutdown();
    }

    /**
     * 🌿🌿🌿🌿🌿
     * 11.删除多个对象,最多一次删除1000个
     * 这里需要注意list大于1000个的情况
     */
    public static void deleteCosObjectByBatch(ArrayList<KeyVersion> keyList, String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketname);

        int size = keyList.size();
        int len = 1000;
        int count = (size + len - 1) / len;


        for (int i = 0; i < count; i++) {
            List<KeyVersion> subList = keyList.subList(i * len, ((i + 1) * len > size ? size : len * (i + 1)));

            deleteObjectsRequest.setKeys(subList);
            try {
                DeleteObjectsResult deleteObjectsResult = cosclient.deleteObjects(deleteObjectsRequest);
                List<DeletedObject> deleteObjectResultArray = deleteObjectsResult.getDeletedObjects();

                System.out.println("已经删除的对象：" + deleteObjectResultArray);
            } catch (MultiObjectDeleteException mde) { // 如果部分产出成功部分失败, 返回MultiObjectDeleteException
                List<DeletedObject> deleteObjects = mde.getDeletedObjects();
                List<DeleteError> deleteErrors = mde.getErrors();

                System.out.println("已经删除的对象：" + deleteObjects);
                System.out.println("删除中报错的对象" + deleteErrors);
            } catch (CosServiceException e) {
                e.printStackTrace();
            } catch (CosClientException e) {
                e.printStackTrace();
            }
        }
        cosclient.shutdown();
    }

    /**
     * 12.删除存储桶
     *
     * @param bucketName
     */
    public static void deleteBucket(String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        if (cosclient.doesBucketExist(bucketname)) {
            cosclient.deleteBucket(bucketname);
        } else {
            System.out.println(bucketname + "存储桶不存在！！！");
        }

        cosclient.shutdown();
    }

    /**
     * 🌿🌿🌿🌿🌿
     * 12.列出存储桶下的所有对象,小于1000个
     * 其中，目录只是其中一个名字特殊，内容为0B的文件，文件之间不存在上下级关系，只是在客户端展示时以上下级关系表示
     *
     * @param bucketName
     */
    public static void listCosObjectBySimple(String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        listObjectsRequest.setBucketName(bucketname);
        listObjectsRequest.setEncodingType("url");
        listObjectsRequest.setMaxKeys(1000);

        ObjectListing objectListing = null;
        try {
            objectListing = cosclient.listObjects(listObjectsRequest);
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        }

        List<COSObjectSummary> cosObjectSummaries = objectListing.getObjectSummaries();
        for (COSObjectSummary cosObjectSummary : cosObjectSummaries) {
            String key = cosObjectSummary.getKey();
            try {
                key = URLDecoder.decode(key, "utf-8");
                System.out.println(key);
            } catch (UnsupportedEncodingException e) {
                continue;
            }
        }
        cosclient.shutdown();
    }

    /**
     * 🌿🌿🌿🌿🌿
     * 12.列出存储桶下的所有对象,上限是1000个
     * 其中，目录只是其中一个名字特殊，内容为0B的文件，文件之间不存在上下级关系，只是在客户端展示时以上下级关系表示
     *
     * @param bucketName
     */
    public static void listCosObjectByBatch(String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        listObjectsRequest.setBucketName(bucketname);
        listObjectsRequest.setEncodingType("url");
        listObjectsRequest.setMaxKeys(5);

        ObjectListing objectListing = null;
        List<String> list = new ArrayList<>();
        do {
            try {
                objectListing = cosclient.listObjects(listObjectsRequest);
            } catch (CosServiceException e) {
                e.printStackTrace();
                return;
            } catch (CosClientException e) {
                e.printStackTrace();
                return;
            }

            List<COSObjectSummary> cosObjectSummaries = objectListing.getObjectSummaries();
            for (COSObjectSummary cosObjectSummary : cosObjectSummaries) {
                String key = cosObjectSummary.getKey();
                try {
                    key = URLDecoder.decode(key, "utf-8");

                    //将指定时间范围的之前的数据记录下来
                    Date date = cosObjectSummary.getLastModified();
                    Calendar calendar1 = Calendar.getInstance();
                    calendar1.setTime(date);

                    Calendar calendar2 = Calendar.getInstance();
                    calendar2.setTime(new Date());
                    calendar2.add(Calendar.MINUTE, -20);

                    if (calendar1.before(calendar2)) {
                        list.add(key);
                    }
                } catch (UnsupportedEncodingException e) {
                    continue;
                }
            }

            String nextMarker = "";
            try {
                String next = objectListing.getNextMarker();
                if (next == null) {
                    break;
                }
                nextMarker = URLDecoder.decode(objectListing.getNextMarker(), "utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return;
            }
            listObjectsRequest.setMarker(nextMarker);
        } while (objectListing.isTruncated());

        System.out.println(list);
        cosclient.shutdown();
    }

    /**
     * 🌿🌿🌿🌿🌿
     * 13.将存储桶下的对象从一个位置移动到另一个位置
     * 主要是目录的移动，也就是添加前缀或者减少前缀<---->等价于修改对象的名字,然后再存储一份
     * 包括在同一个目录下修改名字copy，以及在不同目录下相同名字copy
     */
    public static void copyObjectBySameBucket(String srcPath, String descPath, String bucketName) {
        getProperties();
        getCOSClient();

        if (bucketName != null) {
            bucketname = bucketName;
        }

        Region copyRegion = new Region(region);
        CopyObjectRequest copyObjectRequest = new CopyObjectRequest(copyRegion, bucketname, srcPath, bucketname, descPath);
        try {
            CopyObjectResult copyObjectResult = cosclient.copyObject(copyObjectRequest);
            System.out.println(copyObjectResult.getLastModifiedDate());
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        }
        cosclient.shutdown();
    }

    /**
     * 🌿🌿🌿🌿🌿
     * 14.将存储桶下的一个对象移动到另一个存储桶上
     */
    public static void copyObjectByDifferentBucket(String srcPath, String srcBucketName, String descPath, String descBucketName) {
        getProperties();
        getCOSClient();

        if (srcBucketName != null) {
            bucketname = srcBucketName;
        }
        if (descBucketName != null) {
            anotherbucketname = descBucketName;
        }

        Region copyRegion = new Region(region);
        CopyObjectRequest copyObjectRequest = new CopyObjectRequest(copyRegion, bucketname, srcPath, anotherbucketname, descPath);
        try {
            CopyObjectResult copyObjectResult = cosclient.copyObject(copyObjectRequest);
            System.out.println(copyObjectResult.getLastModifiedDate());
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        }
        cosclient.shutdown();
    }

    /**
     * 🌿🌿🌿🌿🌿
     * 15.使用高级方法，将一个对象进行复制copy
     * 同一个园区拷贝：包括这个桶内的对象移动到这个桶另一个位置，还有这个桶的对象移动到另一个相同地域的桶的位置
     *
     * 高级方法目前只能进行上传，下载和copy操作。
     */
    public static void copyObjectByTransferManagerAndSameRegion(String srcPath, String srcBucketName, String descPath, String descBucketName) {
        getProperties();
        getCOSClient();

        if (srcBucketName != null) {
            bucketname = srcBucketName;
        }
        if (descBucketName != null) {
            anotherbucketname = descBucketName;
        }

        ExecutorService threadPool = Executors.newFixedThreadPool(32);
        TransferManager transferManager = new TransferManager(cosclient, threadPool);

        Region copyRegion = new Region(region);
        CopyObjectRequest copyObjectRequest = new CopyObjectRequest(copyRegion, bucketname, srcPath, anotherbucketname, descPath);
        try {
            Copy copy = transferManager.copy(copyObjectRequest);
            CopyResult copyResult = copy.waitForCopyResult();
            System.out.println(copyResult.getETag());
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        transferManager.shutdownNow();
        cosclient.shutdown();
    }

    /**
     * ⚠️⚠️⚠️
     * 16.使用高级方法，将一个对象进行复制copy
     * 不同一个园区拷贝：将上海的对象，拷贝到广州等
     *
     *  报错没发现，等待官方修复
     *
     * 高级方法目前只能进行上传，下载和copy操作。
     */
    public static void copyObjectByTransferManagerAndDifferentRegion(String srcPath, String srcBucketName, String descPath, String descBucketName) {
        getProperties();
        getCOSClient();

        if (srcBucketName != null) {
            bucketname = srcBucketName;
        }
        if (descBucketName != null) {
            remotebucketname = descBucketName;
        }

        ExecutorService threadPool = Executors.newFixedThreadPool(32);
        TransferManager transferManager = new TransferManager(cosclient, threadPool);

        Region copyRegion = new Region(region);
        Region copyRemoteRegion = new Region(remoteregion);
        COSClient remotecosclient = new COSClient(cred, new ClientConfig(copyRemoteRegion));

        CopyObjectRequest copyObjectRequest = new CopyObjectRequest(copyRegion,bucketname, srcPath, remotebucketname, descPath);
        try {
            Copy copy = transferManager.copy(copyObjectRequest,remotecosclient,null);
            CopyResult copyResult = copy.waitForCopyResult();
            System.out.println(copyResult.getETag());
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        transferManager.shutdownNow();
        cosclient.shutdown();
        remotecosclient.shutdown();
    }
}
